1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
|
// (1) next-auth에서 필요한 타입들을 import
import NextAuth, {
NextAuthOptions, // authOptions에 쓸 타입
Session,
User
} from 'next-auth'
import { JWT } from "next-auth/jwt"
import CredentialsProvider from 'next-auth/providers/credentials'
import { verifyExternalCredentials, verifyOtp, verifyOtpTemp } from '@/lib/users/verifyOtp'
// 1) 모듈 보강 선언
declare module "next-auth" {
/**
* Session 객체를 확장
*/
interface Session {
user: {
/** 우리가 필요로 하는 user id */
id: string
// 기본적으로 NextAuth가 제공하는 name/email/image 필드
name?: string | null
email?: string | null
image?: string | null
companyId?: number | null
techCompanyId?: number | null
domain?: string | null
}
}
/**
* User 객체를 확장
*/
interface User {
id: string
imageUrl?: string | null
companyId?: number | null
techCompanyId?: number | null
domain?: string | null
// 필요한 필드를 추가로 선언 가능
}
}
// (2) authOptions에 NextAuthOptions 타입 지정
export const authOptions: NextAuthOptions = {
providers: [
CredentialsProvider({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'text' },
code: { label: 'OTP code', type: 'text' },
},
async authorize(credentials, req) {
const { email, code } = credentials ?? {}
// OTP 검증
const user = await verifyOtpTemp(email ?? '')
if (!user) {
return null
}
return {
id: String(user.id ?? email ?? "dts"),
email: user.email,
imageUrl: user.imageUrl ?? null,
name: user.name, // DB에서 가져온 실제 이름
companyId: user.companyId, // DB에서 가져온 실제 이름
techCompanyId: (user as any).techCompanyId, // techVendor ID
domain: user.domain, // DB에서 가져온 실제 이름
}
},
}),
// CredentialsProvider({
// name: 'Credentials',
// credentials: {
// email: { label: 'Email', type: 'text' },
// code: { label: 'OTP code', type: 'text' },
// },
// async authorize(credentials, req) {
// const { email, code } = credentials ?? {}
// // OTP 검증
// const user = await verifyOtp(email ?? '', code ?? '')
// if (!user) {
// return null
// }
// return {
// id: String(user.id ?? email ?? "dts"),
// email: user.email,
// imageUrl: user.imageUrl ?? null,
// name: user.name, // DB에서 가져온 실제 이름
// companyId: user.companyId, // DB에서 가져온 실제 이름
// domain: user.domain, // DB에서 가져온 실제 이름
// }
// },
// }),
// 새로 추가할 ID/비밀번호 provider
CredentialsProvider({
id: 'credentials-password',
name: 'Username Password',
credentials: {
username: { label: "Username", type: "text" },
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) { // req 매개변수 추가
if (!credentials?.username || !credentials?.password) {
return null;
}
try {
// 여기서 외부 서비스 API를 호출하여 사용자 인증
const user = await verifyExternalCredentials(
credentials.username,
credentials.password
);
if (user) {
return {
id: String(user.id), // id를 string으로 변환
name: user.name,
email: user.email,
// 첫 번째 provider와 동일한 필드 구조 유지
imageUrl: user.imageUrl ?? null,
companyId: user.companyId,
techCompanyId: user.techCompanyId,
domain: user.domain
};
}
return null;
} catch (error) {
console.error("Authentication error:", error);
return null;
}
}
})
],
// (3) session.strategy는 'jwt'가 되도록 선언
// 필요하다면 as SessionStrategy 라고 명시해줄 수도 있음
// 예) strategy: 'jwt' as SessionStrategy
session: {
strategy: 'jwt',
},
callbacks: {
// (4) 콜백에서 token, user, session 등의 타입을 좀 더 명시해주고 싶다면 아래처럼 destructuring에 제네릭/타입 지정
async jwt({ token, user }: { token: JWT; user?: User }) {
if (user) {
token.id = user.id
token.email = user.email
token.name = user.name
token.companyId = user.companyId
token.techCompanyId = user.techCompanyId
token.domain = user.domain
; (token as any).imageUrl = (user as any).imageUrl
}
return token
},
async session({ session, token }: { session: Session; token: JWT }) {
if (token) {
session.user = {
id: token.id as string,
email: token.email as string,
name: token.name as string,
domain: token.domain as string,
companyId: token.companyId as number,
techCompanyId: token.techCompanyId as number,
image: (token as any).imageUrl ?? null
}
}
return session
},
// redirect 콜백 추가
async redirect({ url, baseUrl }) {
// 상대 경로인 경우 baseUrl을 기준으로 함
if (url.startsWith("/")) {
return `${baseUrl}${url}`;
}
// 같은 도메인인 경우 그대로 사용
else if (new URL(url).origin === baseUrl) {
return url;
}
// 그 외에는 baseUrl로 리다이렉트
return baseUrl;
}
},
}
const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }
|